﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MarvellousWorks.PracticalPattern.FactoryMethod.Example1
{
    #region 简单工厂
    public interface IProduct { }
    public class ProductA : IProduct { }
    public class ProductB : IProduct { }
    public class Factory
    {
        public IProduct Create()
        {
            // 工厂决定到底实例化那个类
            return new ProductA();
        }
    }

    #endregion
    #region 静态工厂
    public enum Category
    {
        A, B
    }

    public static class ProductFactory
    {
        public static IProduct Create(Category category)
        {
            switch (category)
            {
                case Category.A:
                    return new ProductA();
                case Category.B:
                    return new ProductB();
                default:
                    throw new Exception("wrong category");
            }
        }
    }
    #endregion

    #region 抽象工厂
    // 抽象产品类型
   /* public interface IProduct { string Name { get; } }

    // 具体产品类型
    public class ProductA : IProduct { public string Name { get { return "A"; } } }
    public class ProductB : IProduct { public string Name { get { return "B"; } } }

    // 抽象工厂描述
    public interface IFactory
    {
        IProduct Create();
    }

    // 两个实体的工厂类
    public class FactoryA : IFactory
    {
        public IProduct Create() { return new ProductA(); }
    }
    public class FactoryB : IFactory
    {
        public IProduct Create() { return new ProductB(); }
    }*/

    #endregion

    #region 批量工厂
/*
    public class ProductCollection
    {
        private IList<IProduct> data = new List<IProduct>();
        public void Insert(IProduct product) { data.Add(product); }
        public void Insert(IProduct[] products)
        {
            foreach (var item in products)
            {
                data.Add(item);
            }
        }

        public void Remove(IProduct product)
        {
            data.Remove(product);
        }

        public void Clear() { data.Clear(); }

        public IProduct[] Data
        {
            get
            {
                if (data == null || data.Count == 0) return null;
                IProduct[] result = new IProduct[data.Count];
                data.CopyTo(result, 0);
                return result;
            }
        }

        public int Count { get { return data.Count; } }

        // 为了便于操作重载运算符
        public static ProductCollection operator +(ProductCollection collection, IProduct[] products)
        {
            ProductCollection result = new ProductCollection();
            if (collection != null && collection.Count > 0)
            {
                result.Insert(collection.Data);
            }
            if (products != null && products.Length > 0)
            {
                result.Insert(products);
            }
            return result;
        }

        public static ProductCollection operator +(ProductCollection source, ProductCollection target)
        {
            ProductCollection result = new ProductCollection();
            if (source != null && source.Count > 0)
            {
                result.Insert(source.Data);
            }
            if (target != null && target.Count > 0)
            {
                result.Insert(target.Data);
            }
            return result;
        }
    }

    public interface IBatchFactory
    {
        /// <summary>
        /// 批量创建产品
        /// </summary>
        /// <param name="quantity">待加工产品数量</param>
        /// <returns></returns>
        ProductCollection Create(int quantity);
    }

    public class BatchProductFactoryBase<T> : IBatchFactory where T : IProduct, new()
    {
        public virtual ProductCollection Create(int quantity)
        {
            if (quantity < 0) throw new ArgumentOutOfRangeException();
            ProductCollection result = new ProductCollection();
            for (int i = 0; i < quantity; i++)
            {
                result.Insert(new T());
            }
            return result;
        }
    }

    // 两个实体批量生产工厂类型
    public class BatchProductAFactory:BatchProductFactoryBase<ProductA> { }
    public class BatchProductBFactory : BatchProductFactoryBase<ProductB> { }

    // 增加生产指导顾问 Director
    public abstract class DecisionBase
    {
        private readonly IBatchFactory _factory;
        private readonly int _quantity;

        protected DecisionBase(IBatchFactory factory, int quantity)
        {
            _factory = factory;
            _quantity = quantity;
        }

        public virtual IBatchFactory Factory { get { return _factory; } }
        public virtual int Quantity { get { return _quantity; } }
    }

    public abstract class DirectorBase
    {
        protected IList<DecisionBase> decisions = new List<DecisionBase>();

        // 实际项目中最好将每个 Director 需要添加的 Decision 定义在配置文件中，
        // 这样新增加的 Decision 项都在后台完成，而不需要 Assembler 显式调用该方法补充
        protected virtual void Insert(DecisionBase decision)
        {
            if ((decision == null) || (decision.Factory == null) || (decision.Quantity < 0))
            {
                throw new ArgumentException("decision");
            }
            decisions.Add(decision);
        }

        // 便于客户程序使用增加的迭代器
        public virtual IEnumerable<DecisionBase> Decisions { get { return decisions; } }
    }

    // 由 Director 指导的客户程序
    // 在完成了三个辅助类型(ProductCollection Director Decision)的设计之后，就可以设计由 Director 指导生产的新客户程序
    public class ProductADecision : DecisionBase
    {
        public ProductADecision():base(new BatchProductAFactory(),2)
        {
            
        }
    }

    public class ProductBDecision : DecisionBase
    {
        public ProductBDecision() : base(new BatchProductBFactory(), 3)
        {

        }
    }

    public class ProductDirector : DirectorBase
    {
        public ProductDirector()
        {
            base.Insert(new ProductADecision());
            base.Insert(new ProductBDecision());
        }
    }

    public class Client
    {
        // 实际项目中，可以通过 Assembler 从外部把 Director 注入
        private DirectorBase _director = new ProductDirector();
        public IProduct[] Products()
        {
            ProductCollection collection = new ProductCollection();
            foreach (var decision in _director.Decisions)
            {
                collection += decision.Factory.Create(decision.Quantity);
            }
            return collection.Data;
        }
    }*/
    #endregion
}
